package lispinterpreter;

import java.util.LinkedList;


/**
 * La clase modela el objeto LispList a traves de una LinkedList.
 * @author JuanFer
 */
public class LispList {

    public boolean isAtom;

    /**
     *  La linkedlist guarda la secuencia de LispLists que contiene una LispList.
     */
    public LinkedList<LispList> linkedList;

    /**
     * Constructor. Inicializa la linkedlist y pone isAtom false.
     */
    public LispList() {
	linkedList = new LinkedList<LispList>();
	isAtom=false;
    }

    /**
     * Dice si un elemento es un atomo del tipo boolean, float o String.
     * @return Verdadero si es de los tipos especificados.
     */
    public boolean atomVerifier() {
	return (bAtomVerifier() 
		|| fAtomVerifier() 
		|| sAtomVerifier());
    }
    
    /**
     * Agrega un elemento a la linkedlist. Se pueden agregar LispLists y LispAtoms.
     * @param l Elemento a agregar.
     */
    public void addElement(LispList l) {
	linkedList.add(l);
    }
    
    
    /**
     * Dice si un elemento es atomo del tipo boolean.
     * @return Verdadero si es un atomo boolean.
     */
    public boolean bAtomVerifier() {
	if (this.isAtom) {
	    LispAtom atomo = (LispAtom) this;
	    if (atomo.getType() == LispAtom.ATOM_TYPE_BOOLEAN)
		return true;
	}
	return false;
    }

    /**
     * Dice si un elemento es atomo del tipo char: debe ser un parentesis.
     * @return Verdadero si es un parentesis '(' o ')'.
     */
    public boolean cAtomVerifier() {
	if (this.isAtom) {
	    LispAtom atomo = (LispAtom) this;
	    if (atomo.getType() == LispAtom.ATOM_TYPE_PARENTHESIS) {
		return true;
	    }
	}
	return false;
    }

    /**
     * Dice si un elemento es un atomo del tipo float.
     * @return Verdadero si es un atomo float.
     */
    public boolean fAtomVerifier() {
	if (this.isAtom) {
	    LispAtom atomo = (LispAtom) this;
	    if (atomo.getType() == LispAtom.ATOM_TYPE_FLOAT) {
		return true;
	    }
	}
	return false;
    }

    /**
     * Quita y devuleve el primer elemento de la linkedlist asociada.
     * @return Primer elemento de la linkedList.
     */
    public LispList getAndRemoveFirstElement() {
	return linkedList.remove();
    }

    /**
     * Devuelve un elemento de la linkedlist asociada.
     * @param index Indice del elemento a devolver.
     * @return Atomo o LispList en la posicion indicada de la lista.
     */
    public LispList getElement(int index) {
	return linkedList.get(index);
    }

    /**
     * Devuelve el primer elemento de la lista.
     * @return El primer elemento de la lista linkedlist.
     */
    public LispList getFirstElement() {
	return linkedList.getFirst();
    }

    /**
     * Devuelve el tamaño de la linkedlist asociada al elemento.
     * @return Tamaño de linkedlist.
     */
    public int getSize() {
	return linkedList.size();
    }

    /**
     * Indica la ubicación de la variable en la lista LispList por medio de la
     * posición desde el inicio
     * 
     * @return posición de la variable desde el inicio de la lista.
     */
    public int getVariableLocation() {
	Object arr[] = linkedList.toArray();
	LispAtom lispAtom;
	int index = 0;

	for (int i = 1; i < this.size(); i++) { // Debe ser 1 para quitar el
						// operando
	    lispAtom = (LispAtom) arr[i];
	    if (LispAtom.ATOM_TYPE_STRING == lispAtom.getType()) {
		index = i;
		break;
	    }

	}
	return index;
    }

    /**
     * Devuelve el valor de el boolean isAtom, es falso si es LipsList y verdadero 
     * si es Atomo.
     * @return boolean isAtom.
     */
    public boolean isAtom() {
	return isAtom;
    }

    /**
     * @pre LispList esta formado de atomos, sin operador.
     * @param tipo
     * @return
     */
    public boolean isParameter(int tipo) {
	boolean test = true;
	Object arreglo[] = linkedList.toArray();
	LispAtom lispObject;
	for (int i = 1; i < this.size(); i++) {
	    lispObject = (LispAtom) arreglo[i];
	    test = test && (tipo == lispObject.getType());
	}
	return test;
    }

    public boolean isParsed() {
	Object arreglo[] = linkedList.toArray();
	boolean test = true;
	LispList lispObject = new LispList();

	for (int i = 0; i < arreglo.length; i++) {
	    lispObject = (LispList) arreglo[i];
	    if (lispObject.isAtom) {
		test = test && !lispObject.cAtomVerifier();
	    }
	}
	return test;
    }

    /**
     * Indica si una lista es simple.
     * 
     * @return true sí y sólo si la lista está compuesta sólo por átomos.
     */
    public boolean isSimple() {
	Object arreglo[] = linkedList.toArray();
	boolean test = true;
	LispList lispObject = new LispList();
	for (int i = 0; i < arreglo.length; i++) {
	    lispObject = (LispList) arreglo[i];
	    test = test && (lispObject.isAtom());
	}
	return test;
    }

    
    /**
     * Imprime los valores de un LispList en notacion prefix.
     */
    public void printLispList() {

	for (int i = 0; i < this.size(); i++) {
	    LispList sublista = this.getElement(i);

	    if (sublista.sAtomVerifier()) {
		LispAtom atomo = (LispAtom) sublista;
		atomo.printAtom();
	    }
	    if (sublista.fAtomVerifier()) {
		LispAtom atomo = (LispAtom) sublista;
		atomo.printAtom();
	    } else {
		sublista.printLispList();
	    }
	}
    }

    /**
     * Quita un elemento de la lista linkedlist.
     * @param index  Indice del elemento a remover.
     */
    public void removeElement(int index) {
	linkedList.remove(index);
    }
    
    /**
     * Reemplaza el indice de la lista especificado por el objeto mandado como parametro.
     * @param index Indice del elemento a reemplazar.
     * @param obj Objeto LispList reemplazo.
     */
    public void replaceElement(int index, LispList obj) {
	linkedList.set(index, obj);
    }

    /**
     * Dice si el LispList es un atomo del tipo String.
     * @return Verdadero si es un atomo String.
     */
    public boolean sAtomVerifier() {
	if (this.isAtom) {
	    LispAtom atomo = (LispAtom) this;
	    if (atomo.getType() == LispAtom.ATOM_TYPE_STRING
		    || atomo.getType() == LispAtom.ATOM_TYPE_PARENTHESIS) {
		return true;
	    }
	}
	return false;
    }

//    /**
//     * 
//     * @param isAtom
//     */
//    public void setAtom(boolean isAtom) {
//	this.isAtom = isAtom;
//    }

    /**
     * Devuelve el tamaño de la linkedlist.  Pendiente: Reemplazar metodo por getSize().
     */
    public int size() {
	return linkedList.size();
    }

}
